/*
 * semantic.c
 *
 * Implements all functions that participate in semantic analysis.
 */


#include "shared.h"
#include "semantic.h"
#include "rulefuncs.h"
#include "usrdef.h"
#include "symtab.h"



/* ----------------------------------------------------------------------- 
 * Assigns values to the members of the expression_data variable
 * ----------------------------------------------------------------------- 
 */
void set_expression_data(struct expression_data_t *ed, 
		char *typename,
		float val)
{
	ed->val = val;
	ed->type = typename;
}



/* -----------------------------------------------------------------------
 * Adds the variables listed in the type_definition_list into the
 * symbol table
 * -----------------------------------------------------------------------
 */
void create_variables(struct class_list_t *cl, int scope, int pass_number)
{
	struct variable_declaration_list_t *temp;

	temp = cl->cb->vdl;
	/* go through the variable_declaration_list */
	while (temp != NULL) {
		create_variables_from_variable_declaration_list(temp, cl->ci->id, NULL, scope, 1, pass_number);
		temp = temp->next;
	}

}

void create_variables_from_variable_declaration_list(struct variable_declaration_list_t *vdl, char *class, char *name_it_cannot_hold, int scope, int creating_variables_in_class, int pass_number)
{
	struct identifier_list_t *il;
	struct type_denoter_t *td;

	il = vdl->vd->il;
	td = vdl->vd->tden;

	/* go through each identifier */
	while (il != NULL) {
		struct symbol_t *s;

		/* if there's no such symbol */
		s = symtab_variable_lookup(il->id, class, scope);

		if (name_it_cannot_hold != NULL)
			if (strcmp(tolower(il->id),tolower(name_it_cannot_hold)) == 0) {
				/* This variable has the same name as the function it
				   belongs to. This is not acceptable. This variable name is
				   reserved to indicate the return value of the function. */
				error_variable_name_reserved_for_function_return_value(vdl->vd->line_number, il->id);
				goto continue_loop;
			}

		if (s == NULL) {

			/* if this is an identifier, it must describe an already
			   defined usrdef type */
			if (td->type == TYPE_DENOTER_T_IDENTIFIER) {
				struct usrdef_t *u;

				u = usrdef_lookup(td->data.id);
				if (u != NULL) {
					/* if there is such a datatype */
					struct symbol_t *new;

					/* Don't permit variables named True or False. */
					if (check_variable_tf(il->id) == 1) {
						if (pass_number == 2)
							error_variable_name_invalid(vdl->vd->line_number, il->id);
					} else {
						new = symtab_insert(il->id, class, scope, vdl->vd->line_number, name_it_cannot_hold);
						new->type = SYMTAB_VARIABLE;
						new->data.type_name = td->name;
					}
				} else { /* complain */
					if (pass_number == 2)
						error_type_not_defined(vdl->vd->line_number, td->data.id);
				}
			} else {
				/* if this is an array_type, need to add this anonymous usrdef
				   type */
				struct symbol_t *new;

				/* must verify that the datatype for this variable is valid */
				check_type_denoter(vdl->vd->line_number, td);

				/* Must also verify that the range supplied for the array
				   bounds is valid */
				if (vdl->vd->tden->data.at->r->min->ui > vdl->vd->tden->data.at->r->max->ui)
					error_array_range_invalid(vdl->vd->line_number,
							vdl->vd->tden->data.at->r->min->ui,
							vdl->vd->tden->data.at->r->max->ui);

				new = symtab_insert(il->id, class, scope, vdl->vd->line_number, name_it_cannot_hold);
				new->type = SYMTAB_VARIABLE;
				new->data.type_name = td->name;
			}
		}
		else {
			/* If there is one and it's in the same scope (local variable
			   declaration) or different class (a base class that was
			   extended) */
			if (pass_number == 1) {
				if (!symtab_variable_global(s,  scope) || 
						((strcmp(tolower(s->class),tolower(class)) != 0) && creating_variables_in_class))
					error_variable_already_declared(vdl->vd->line_number, il->id, s->line_number);
			}
		}

continue_loop:
		il = il->next;
	}
}


/* -----------------------------------------------------------------------
 * Adds the functions listed in the class_list into the symbol table
 * -----------------------------------------------------------------------
 */
void create_functions(struct class_list_t *cl, int scope, int pass_number)
{
	struct func_declaration_list_t *temp;

	temp = cl->cb->fdl;

	/* go through all elements of the func_declaration_list */
	while (temp != NULL) {
		struct symbol_t *s;
		struct symbol_t *new;
		int function_scope;

		/* if there's no such symbol */
		s = symtab_function_lookup(temp->fd->fh, cl->ci->id);

		if (pass_number == 1) {
			/* This is the first pass. */

			if (s == NULL) {
				struct symbol_t *new_var;
				struct variable_declaration_list_t *temp_vdl;

				function_scope = symtab_new_scope();
				new = symtab_insert(temp->fd->fh->id, cl->ci->id, function_scope, temp->fd->line_number, NULL);
				new->type = SYMTAB_FUNCTION;
				new->data.fd = symtab_new_function_description();
				new->data.fd->fd = temp->fd;

				process_function_heading(new->data.fd->fd->line_number,
						new->data.fd->fd->fh,
						cl->ci->id,
						function_scope,
						pass_number);

				/* Create an extra variable with the same name as the
				   function. It will be used for setting the return value of
				   the function .*/
                if (!(temp->fd->fh->res))
				    new_var = symtab_insert(temp->fd->fh->id, cl->ci->id, function_scope, temp->fd->line_number, NULL);
                else
				    new_var = symtab_insert(temp->fd->fh->id, cl->ci->id, function_scope, temp->fd->line_number, temp->fd->fh->id);

				new_var->type = SYMTAB_VARIABLE;
				new_var->data.type_name = temp->fd->fh->res;

				/* process the variable_declaration_part */
				temp_vdl = temp->fd->fb->vdl;
				while (temp_vdl != NULL) {
					create_variables_from_variable_declaration_list(temp_vdl, cl->ci->id, new->data.fd->fd->fh->id, function_scope, 0, pass_number);
					temp_vdl = temp_vdl->next;
				}
				/* Don't process the statement_sequence. It's still the first
				   pass and we are still creating the data types. */
			} else { /* This is a duplicate function declaration. Don't
				    complain. Will complain in the second pass. */
				;
			}
		} else {
			/* This is the second pass. */

			if (s->seen_at_least_once == 1) {
				error_function_already_declared(temp->fd->line_number, temp->fd->fh->id, s->line_number);
			} else {
				int function_return_value_set = 0;

				s->seen_at_least_once = 1;

				/* This is the second pass. Should check again if the used
				   variables are declared. */
				new = s;
				process_function_heading(new->data.fd->fd->line_number,
						new->data.fd->fd->fh,
						cl->ci->id,
						s->scope,
						pass_number);

				/* process the statement_sequence */
				check_if_used_variables_are_declared(temp->fd->fb->ss, cl->ci->id, temp->fd->fh->id, s->scope, pass_number, &function_return_value_set);

				/* If this function has a return value, and one has not been
				   set this is an error */
				if (temp->fd->fh->res != NULL &&
						function_return_value_set == 0) {
					error_function_return_value_not_set(new->data.fd->fd->line_number, temp->fd->fh->id);
				}
			}
		}

		temp = temp->next;
	}
}



/* ----------------------------------------------------------------------- 
 * Makes a few checks on the type_denoter
 * - need to make sure that all the elements of the type_denoter of
 *   this type_definition exist (in case of arrays)
 * - need to ensure that if the type_definition is an identifier,
 *   it is pointed to the correct type from the usrdef table or complain
 * ----------------------------------------------------------------------- 
 */
void check_type_denoter(int line_number,
		struct type_denoter_t *td)
{
	if (td->type == TYPE_DENOTER_T_IDENTIFIER)
		check_type_denoter_valid(line_number, td);
	else 
		check_type_denoter_elements_exist(line_number, td);
}



/* ----------------------------------------------------------------------- 
 * Checks if the type_denoter passed in already exists (is valid)
 * ----------------------------------------------------------------------- 
 */
void check_type_denoter_valid(int line_number,
		struct type_denoter_t *td)
{
	struct usrdef_t *usr;

	usr = usrdef_lookup(td->name);
	if (usr == NULL)  { /* complain */
		error_type_not_defined(line_number, td->name);
	} else 
		td = usr->td;
}



/* ----------------------------------------------------------------------- 
 * Checks if the elements encapsulated in this type_denoter exist
 * ----------------------------------------------------------------------- 
 */
void check_type_denoter_elements_exist(int line_number,
		struct type_denoter_t *td)
{
	struct usrdef_t *u;

	if (td->type == TYPE_DENOTER_T_ARRAY_TYPE) {
		/* if this anonymous userdefined data type is not present in the
		   current usrdef table, insert it; it could be the case that it's
		   already there if another identifier happened to add it there
		   (e.g. case TYPE class = ARRAY[4..9] OF integer; ) */
		u = usrdef_lookup(td->name);
		if (u == NULL) {
			u = usrdef_insert(td->name, line_number);
			u->td = td;
		}
		check_type_denoter(line_number, td->data.at->td);
	}
}



/* ----------------------------------------------------------------------- 
 * Checks if the variables used in the statement_sequence are declared
 * ----------------------------------------------------------------------- 
 */
void check_if_used_variables_are_declared(struct statement_sequence_t *ss, char *class, char *name_it_cannot_hold, int scope, int pass_number, int *function_return_value_set)
{
	struct statement_sequence_t *temp;

	temp = ss;

	/* check all statements */
	while (temp != NULL) {
		check_used_variables_in_statement(temp->s, class, name_it_cannot_hold, scope, pass_number, function_return_value_set);
		temp = temp->next;
	}
}



/* ----------------------------------------------------------------------- 
 * Checks if the variables used in the statement are declared
 * ----------------------------------------------------------------------- 
 */
void check_used_variables_in_statement(struct statement_t *s, char *class, char *name_it_cannot_hold, int scope, int pass_number, int *function_return_value_set)
{
	if (s->type == STATEMENT_T_ASSIGNMENT) {
		int retval_va;
		int retval_e;
		char *class_to_check;

		retval_va = check_used_variables_in_variable_access(s->line_number, s->data.as->va, class, name_it_cannot_hold, scope, pass_number, 1, function_return_value_set, &class_to_check, 1);
		if (s->data.as->e != NULL) {
			retval_e = check_used_variables_in_expression(s->line_number, s->data.as->e, class, name_it_cannot_hold, scope, pass_number, 0, function_return_value_set);

			/* Continue type-checking only if there were no errors so far. */
			if (retval_va == 0 && retval_e == 0) {
				if (!check_types(s->data.as->va->expr, s->data.as->e->expr)) {
					/* It could be the case that one type is a base class of the
					   other. Permit that if the left-hand-side(LHS) is bigger than
					   the right-hand-side(RHS). Meaning if the LHS extends the
					   RHS. */
					int class_extends_retval = symtab_class_extends(s->data.as->va->expr->type, s->data.as->e->expr->type);
					if (class_extends_retval == 0) {
						/* LHS does not extend the RHS: RHS is not a base class
						   of the LHS */
						error_class_not_base_class(s->line_number,
								s->data.as->va->expr->type,
								s->data.as->e->expr->type);
					} else if (class_extends_retval == -1) {
						if (pass_number == 2)
							error_type_mismatch(s->line_number,
									s->data.as->va->expr->type,
									s->data.as->e->expr->type);
					}
				}
			} else {
				/* An error was propagated. It could be a type error mismatch,
				   hence we should check for it. But it's also possible that
				   one of these expressions is not available: e.g. because one
				   of the variables used in this expression is not declared,
				   hence we should not try to emit a type error at all. */
				if (s->data.as->va->expr != NULL &&
						s->data.as->e->expr != NULL) {
					/* The variables used in the expressions were all available */
					if (!check_types(s->data.as->va->expr, s->data.as->e->expr)) {
						error_type_mismatch(s->line_number,
								s->data.as->va->expr->type,
								s->data.as->e->expr->type);
					}
				}
			}
		} else {
			/* This is a case where we have a new object_instantiation */
			struct symbol_t *sym;
			struct actual_parameter_list_t *apl;

			apl = s->data.as->oe->apl;
			while (apl != NULL) {
				check_used_variables_in_expression(0, apl->ap->e1, class, name_it_cannot_hold, scope, pass_number, 0, function_return_value_set);

				if (apl->ap->e2 != NULL) {
					check_used_variables_in_expression(0, apl->ap->e2, class, name_it_cannot_hold, scope, pass_number, 0, function_return_value_set);

					if (apl->ap->e3 != NULL)
						check_used_variables_in_expression(0, apl->ap->e3, class, name_it_cannot_hold, scope, pass_number, 0, function_return_value_set);
				}
				apl = apl->next;
			}

			struct function_designator_t *newFD = malloc(sizeof(struct function_designator_t));
			newFD->id = s->data.as->oe->id;
			newFD->apl = s->data.as->oe->apl;

			/* Check if the method designator is a function in that class,
			   instead of the local class. */
			sym = symtab_function_desig_lookup(newFD, s->data.as->oe->id);

			if (sym == NULL && s->data.as->oe->apl != NULL) {
				// Only error out if the constructor provided has parameters and there isn't one defined with parameters
				error_function_not_declared(0, s->data.as->oe->id);
			} 
		}
	} else if (s->type == STATEMENT_T_SEQUENCE) {
		check_if_used_variables_are_declared(s->data.ss, class, name_it_cannot_hold, scope, pass_number, function_return_value_set);
	} else if (s->type == STATEMENT_T_IF) {
		check_used_variables_in_expression(s->line_number, s->data.is->e, class, name_it_cannot_hold, scope, pass_number, 0, function_return_value_set);
		check_used_variables_in_statement(s->data.is->s1, class, name_it_cannot_hold, scope, pass_number, function_return_value_set);
		check_used_variables_in_statement(s->data.is->s2, class, name_it_cannot_hold, scope, pass_number, function_return_value_set);
	} else if (s->type == STATEMENT_T_WHILE) {
		check_used_variables_in_expression(s->line_number, s->data.ws->e, class, name_it_cannot_hold, scope, pass_number, 0, function_return_value_set);
		check_used_variables_in_statement(s->data.ws->s, class, name_it_cannot_hold, scope, pass_number, function_return_value_set);
	} else if (s->type == STATEMENT_T_PRINT) {
		char *class_to_check;

		check_used_variables_in_variable_access(s->line_number, s->data.ps->va, class, name_it_cannot_hold, scope, pass_number, 0, function_return_value_set, &class_to_check, 1);
	} else if (s->type == STATEMENT_T_PRIMARY) {
		char *class_to_check;
		check_used_variables_in_primary(s->line_number, s->data.pr, class, name_it_cannot_hold, scope, pass_number, 0, function_return_value_set);
	}
}



/* ----------------------------------------------------------------------- 
 * Checks if the variables used in the variable_access are declared
 * ----------------------------------------------------------------------- 
 */
int check_used_variables_in_variable_access(int line_number,
		struct variable_access_t *va,
		char *class,
		char *name_it_cannot_hold,
		int scope,
		int pass_number,
		int in_left_hand_side,
		int *function_return_value_set,
		char **class_to_check,
		int depth)
{
	int local_error = 0;

	if (va->type == VARIABLE_ACCESS_T_IDENTIFIER) {
		struct symbol_t *s;
		struct expression_data_t *ed;


		/* "this" is a special case meaning the current class, instead of
		   a local function variable. */
		if (strcmp(tolower(va->data.id), "this") == 0) {
			*class_to_check = class;

			/* used for type checking */
			ed = new_expression_data();
			set_expression_data(ed, class, 0);
			va->expr = ed;

			goto out;
		}

		s = symtab_variable_lookup(va->data.id, class, scope);
		if (s != NULL) {
			struct usrdef_t *u;

			/* Mark the class of this identifier, since it will be used from
			   a possible caller attribute designator */
			*class_to_check = s->data.type_name;

			if (strcmp(tolower(va->data.id), tolower(name_it_cannot_hold)) == 0) {
				/* It could be the case that this variable has the same name
				   as the function name, which would make this effectively a
				   return statement (sets the return value of the
				   function). That would only be valid if this is the left
				   hand side of the expression. */
				if (in_left_hand_side) {
					/* Great, this is valid. We'll handle it like other
					   variables by falling through. Flag the fact that a return
					   value was set for this function. */
					*function_return_value_set = 1;
				} else {
					local_error = 1;
					error_variable_name_reserved_for_function_return_value(line_number, va->data.id);
					goto out;
				}

			}

			u = usrdef_lookup(s->data.type_name);

			if (u != NULL) {
				va->recordname =
					(char *) malloc (strlen(va->data.id) + 1); /* +1 for '\0' */
				strcpy(va->recordname, va->data.id);

				/* used for type checking */
				ed = new_expression_data();
				set_expression_data(ed, u->name, 0);
				va->expr = ed;
			} else {
				if (pass_number == 2) {
					local_error = 1;
					error_type_not_defined(line_number, s->data.type_name);
				}
			}
		} else
			/* It could be the case that this is the "True" or "False"
			   identifiers for boolean expressions. If so, they should be
			   permitted. */
			if (check_variable_tf(va->data.id) == 1) {
				/* Permit the identifier */
				ed = new_expression_data();
				va->expr = ed;
				if (strcmp(tolower(va->data.id), tolower(BOOLEAN_VALUE_TRUE)) == 0)
					set_expression_data(ed, PRIMITIVE_TYPE_NAME_BOOLEAN, 1);
				else
					set_expression_data(ed, PRIMITIVE_TYPE_NAME_BOOLEAN, 0);
			} else {
				/* Else it's simply a variable that's not declared. */
				if (pass_number == 2) {
					// DON'T ERROR YET. We need to see if this is just a function call first!!!!!!!!!!!!!!!!!!!!!!
					struct function_designator_t *newFD = malloc(sizeof(struct function_designator_t));
					newFD->id = va->data.id;
					struct symbol_t *f = symtab_function_desig_lookup(newFD, class);
					if (f == NULL) {
						// FINE - now we can error out!
						local_error = 1;
						error_variable_not_declared(line_number, va->data.id);
					} else {
						struct expression_data_t *ed;
						ed = new_expression_data();
						set_expression_data(ed, f->data.fd->fd->fh->res, 0);
						va->expr = ed;
					}
				}
			}
	} else if (va->type == VARIABLE_ACCESS_T_INDEXED_VARIABLE) {
		char *new_class_to_check;

		check_used_variables_in_indexed_variable(line_number, va->data.iv, class, name_it_cannot_hold, scope, pass_number, in_left_hand_side, function_return_value_set, &new_class_to_check, depth);

		/* Mark the class of this identifier, since it will be used from a
		   possible caller */
		*class_to_check = new_class_to_check;

		/* used for type checking */
		va->expr = va->data.iv->expr;

		{
			struct index_expression_list_t *iel;
			struct index_expression_list_t *iel_last;
			long iel_counter = 0;

			/* If this is an array of arrays, set the data type correctly to
			   be the basic type of the innermost nested array. */
			iel = va->data.iv->iel;
			while (iel != NULL) {
				iel_counter++;
				iel_last = iel;
				iel = iel->next;
			}
			if (iel_counter > 1)
				va->expr = iel_last->expr;
		}

	} else if (va->type == VARIABLE_ACCESS_T_ATTRIBUTE_DESIGNATOR) {
		/* Lookup the class of the object */
		struct symbol_t *s;
		char *new_class_to_check;

		if (pass_number == 2) {
			/* We don't have enough information to handle this, and must
			   wait until the second pass. */
			struct usrdef_t *u;
			int scope_of_new_class_to_check;

			check_used_variables_in_variable_access(line_number, va->data.ad->va, class, name_it_cannot_hold, scope, pass_number, in_left_hand_side, function_return_value_set, &new_class_to_check, depth + 1);

			/* Check if the attribute designator is a variable in that
			   class, instead of the local function. */
			u = usrdef_lookup(new_class_to_check);
			scope_of_new_class_to_check = u->scope;
			s = symtab_variable_lookup(va->data.ad->id, new_class_to_check, scope_of_new_class_to_check);
			if (s == NULL) {
				// This might be trying to call a function that has no parameters - check and see
				struct function_designator_t *newFD = malloc(sizeof(struct function_designator_t));
				newFD->id = va->data.ad->id;
				struct symbol_t *f = symtab_function_desig_lookup(newFD, new_class_to_check);
				if (f == NULL) {
					// Nope, not a function either, so error out		
					local_error = 1;
					error_variable_not_declared(line_number, va->data.ad->id);
				} else {
					struct expression_data_t *ed;
					ed = new_expression_data();
					set_expression_data(ed, f->data.fd->fd->fh->res, 0);
					va->expr = ed;
				}	
			} else {
				struct expression_data_t *ed;
				struct usrdef_t *u;

				u = usrdef_lookup(s->data.type_name);

				/* Mark the class of this attribute designator, since it will
				   be used from a possible caller attribute designator */
				*class_to_check = s->data.type_name;

				/* used for type checking */
				ed = new_expression_data();
				set_expression_data(ed, u->name, 0);
				va->expr = ed;
			}
		}
	} else if (va->type == VARIABLE_ACCESS_T_METHOD_DESIGNATOR) {
		struct symbol_t *s;
		struct actual_parameter_list_t *apl;
		char *new_class_to_check;

		check_used_variables_in_variable_access(line_number, va->data.md->va, class, name_it_cannot_hold, scope, pass_number, in_left_hand_side, function_return_value_set, &new_class_to_check, depth + 1);

		apl = va->data.md->fd->apl;
		while (apl != NULL) {
			check_used_variables_in_expression(line_number, apl->ap->e1, class, name_it_cannot_hold, scope, pass_number, in_left_hand_side, function_return_value_set);

			if (apl->ap->e2 != NULL) {
				check_used_variables_in_expression(line_number, apl->ap->e2, class, name_it_cannot_hold, scope, pass_number, in_left_hand_side, function_return_value_set);

				if (apl->ap->e3 != NULL)
					check_used_variables_in_expression(line_number, apl->ap->e3, class, name_it_cannot_hold, scope, pass_number, in_left_hand_side, function_return_value_set);
			}
			apl = apl->next;
		}

		/* Check if the method designator is a function in that class,
		   instead of the local class. */
		s = symtab_function_desig_lookup(va->data.md->fd, new_class_to_check);

		if (s == NULL || !symtab_variable_global(s, scope)) {
			local_error = 1;
			error_function_not_declared(line_number, va->data.md->fd->id);
		} else {
			*class_to_check = s->data.type_name;
			struct expression_data_t *ed;
			ed = new_expression_data();
			set_expression_data(ed, s->data.fd->fd->fh->res, 0);
			va->expr = ed;
		}
	}

out:
	return local_error;
}



/* ----------------------------------------------------------------------- 
 * Checks if the variables used in the indexed_variable are declared
 * ----------------------------------------------------------------------- 
 */
void check_used_variables_in_indexed_variable(int line_number,
		struct indexed_variable_t *iv,
		char *class,
		char *name_it_cannot_hold,
		int scope,
		int pass_number,
		int in_left_hand_side,
		int *function_return_value_set,
		char **class_to_check,
		int depth)
{
	struct usrdef_t *u;
	struct usrdef_t *last_u;
	char *type_name;
	long iel_counter = 0;
	struct index_expression_list_t *iel;
	char *new_class_to_check;

	check_used_variables_in_variable_access(line_number, iv->va, class, name_it_cannot_hold, scope, pass_number, in_left_hand_side, function_return_value_set, &new_class_to_check, depth + 1);
	check_used_variables_in_index_expression_list(line_number, iv->iel, class, name_it_cannot_hold, scope, pass_number, in_left_hand_side, function_return_value_set);

	/* do type checking */
	type_name = new_class_to_check;
	u = usrdef_lookup(type_name);
	last_u = u;

	/* Verify that the array index is within the array's bounds , for
	   each of it's dimensions. */
	iel = iv->iel;
	while (iel != NULL) {

		if(u->td->type != TYPE_DENOTER_T_ARRAY_TYPE) {
			error_indexed_variable_not_an_array(line_number, iv->va->recordname);
			return;
		}

		/* Verify that the array index is an integer */
		if (strcmp(tolower(iel->expr->type), tolower(PRIMITIVE_TYPE_NAME_INTEGER)) != 0) {
			error_array_index_is_not_integer(line_number, iv->va->data.id);
		}

		/* Verify that the array index is within the array's bounds */
		if (iel->e->expr->val < u->td->data.at->r->min->ui ||
				iel->e->expr->val > u->td->data.at->r->max->ui)
			error_array_index_out_of_bounds(line_number,
					iel->e->expr->val,	    
					u->td->data.at->r->min->ui,
					u->td->data.at->r->max->ui);

		type_name = u->td->data.at->td->name;
		last_u = u;
		u = usrdef_lookup(type_name);

		/* Mark the class of this indexed_variable, since it will be used
		   from a possible caller */
		*class_to_check = type_name;

		iel = iel->next;
	}

	iel_counter = index_expression_list_count(iv->iel);
	if (iel_counter > 1) {
		/* If no errors were found, setup the returned expression. The type
		   of the innermost array (in the case of arrays of arrays) is used
		   as the returned type, since that was set in
		   check_used_variables_in_variable_access on an indexed_variable
		   access. */
		{
			struct expression_data_t *ed;

			ed = new_expression_data();
			set_expression_data(ed, last_u->td->data.at->td->name, 0);
			iv->expr = ed;
		}
	} else {
		/* If no errors were found, setup the returned expression. */
		struct expression_data_t *ed;

		ed = new_expression_data();
		if (last_u->td->type == TYPE_DENOTER_T_ARRAY_TYPE)
			set_expression_data(ed, last_u->td->data.at->td->name, 0);
		else
			set_expression_data(ed, last_u->name, 0);
		iv->expr = ed;
	}

}



/* ----------------------------------------------------------------------- 
 * Checks if the variables used in the index_expression_list are declared
 * ----------------------------------------------------------------------- 
 */
void check_used_variables_in_index_expression_list(
		int line_number,
		struct index_expression_list_t *iel,
		char *class,
		char *name_it_cannot_hold, 
		int scope,
		int pass_number,
		int in_left_hand_side,
		int *function_return_value_set)
{
	if (iel->next != NULL) {
		check_used_variables_in_index_expression_list(line_number, iel->next, class, name_it_cannot_hold, scope, pass_number, in_left_hand_side, function_return_value_set);
		check_used_variables_in_expression(line_number, iel->e, class, name_it_cannot_hold, scope, pass_number, in_left_hand_side, function_return_value_set);
		iel->expr = iel->e->expr;
	}
	else {
		check_used_variables_in_expression(line_number, iel->e, class, name_it_cannot_hold, scope, pass_number, in_left_hand_side, function_return_value_set);
		iel->expr = iel->e->expr;
	}

}



/* ----------------------------------------------------------------------- 
 * Checks if the variables used in the expression are declared
 * ----------------------------------------------------------------------- 
 */
int check_used_variables_in_expression(int line_number,
		struct expression_t *e,
		char *class,
		char *name_it_cannot_hold, 
		int scope,
		int pass_number,
		int in_left_hand_side,
		int *function_return_value_set)
{
	int local_error = 0;
	int retval_se1;
	int retval_se2;

	retval_se1 = check_used_variables_in_simple_expression(line_number, e->se1, class, name_it_cannot_hold, scope, pass_number, in_left_hand_side, function_return_value_set);

	if (retval_se1 == 0) {

		if (e->se2 != NULL) {
			retval_se2 = check_used_variables_in_simple_expression(line_number, e->se2, class, name_it_cannot_hold, scope, pass_number, in_left_hand_side, function_return_value_set);

			if (retval_se2 == 0) {
				/* do type checking */

				if (check_types_simple_expression(e->se1, e->se2)) {
					if (e->se2 == NULL)
						e->expr = e->se1->expr;
					else {
						if (e->relop == EQUAL) {
							e->expr = new_expression_data();

							if (e->se1->expr->val == e->se2->expr->val) {
								set_expression_data(e->expr, PRIMITIVE_TYPE_NAME_BOOLEAN, 1);
							} else {
								set_expression_data(e->expr, PRIMITIVE_TYPE_NAME_BOOLEAN, 0);
							}
						} else if (e->relop == NOTEQUAL) {
							e->expr = new_expression_data();

							if (e->se1->expr->val != e->se2->expr->val) {
								set_expression_data(e->expr, PRIMITIVE_TYPE_NAME_BOOLEAN, 1);
							} else {
								set_expression_data(e->expr, PRIMITIVE_TYPE_NAME_BOOLEAN, 0);
							}
						} else if (e->relop == LT) {
							e->expr = new_expression_data();

							if (e->se1->expr->val < e->se2->expr->val) {
								set_expression_data(e->expr, PRIMITIVE_TYPE_NAME_BOOLEAN, 1);
							} else {
								set_expression_data(e->expr, PRIMITIVE_TYPE_NAME_BOOLEAN, 0);
							}
						} else if (e->relop == GT) {
							e->expr = new_expression_data();

							if (e->se1->expr->val > e->se2->expr->val) {
								set_expression_data(e->expr, PRIMITIVE_TYPE_NAME_BOOLEAN, 1);
							} else {
								set_expression_data(e->expr, PRIMITIVE_TYPE_NAME_BOOLEAN, 0);
							}
						} else if (e->relop == LE) {
							e->expr = new_expression_data();

							if (e->se1->expr->val <= e->se2->expr->val) {
								set_expression_data(e->expr, PRIMITIVE_TYPE_NAME_BOOLEAN, 1);
							} else {
								set_expression_data(e->expr, PRIMITIVE_TYPE_NAME_BOOLEAN, 0);
							}
						} else if (e->relop == GE) {
							e->expr = new_expression_data();

							if (e->se1->expr->val >= e->se2->expr->val) {
								set_expression_data(e->expr, PRIMITIVE_TYPE_NAME_BOOLEAN, 1);
							} else {
								set_expression_data(e->expr, PRIMITIVE_TYPE_NAME_BOOLEAN, 0);
							}
						} else {
							error_unknown(line_number);
						}
					}
				} else {
					e->expr = new_expression_data();
					set_expression_data(e->expr,
							PRIMITIVE_TYPE_NAME_UNKNOWN,
							0);
					local_error = 1;
					error_type_mismatch(line_number, e->se1->expr->type, e->se2->expr->type);
				}
			} else {
				e->expr = e->se2->expr;
				local_error = 1;
			}
		} else {
			e->expr = e->se1->expr;
		}
	} else {
		e->expr = e->se1->expr;
		local_error = 1;
	}

	return local_error;
}



/* ----------------------------------------------------------------------- 
 * Checks if the variables used in the simple_expression are declared
 * ----------------------------------------------------------------------- 
 */
int check_used_variables_in_simple_expression(int line_number,
		struct simple_expression_t *se,
		char *class,
		char *name_it_cannot_hold, 
		int scope,
		int pass_number,
		int in_left_hand_side,
		int *function_return_value_set)
{
	int local_error = 0;

	if (se->next != NULL) {
		int retval_t;
		int retval_se;

		retval_t = check_used_variables_in_term(line_number, se->t, class, name_it_cannot_hold, scope, pass_number, in_left_hand_side, function_return_value_set);
		retval_se = check_used_variables_in_simple_expression(line_number, se->next, class, name_it_cannot_hold, scope, pass_number, in_left_hand_side, function_return_value_set);

		if (retval_t == 0 && retval_se == 0) {

			/* do type checking */
			if (check_types_term(se->t, se->next->t)) {
				se->expr = new_expression_data();

				if (se->addop == PLUS) {
					set_expression_data(se->expr,
							se->t->expr->type,
							se->t->expr->val + se->next->expr->val);
				} else if (se->addop == MINUS) {
					set_expression_data(se->expr,
							se->t->expr->type,
							se->t->expr->val - se->next->expr->val);
				} else if (se->addop == OR) {
					set_expression_data(se->expr,
							PRIMITIVE_TYPE_NAME_INTEGER,
							(int) se->t->expr->val | (int) se->next->expr->val);
				} else {
					error_unknown(line_number);
				}
			} else {
				se->expr = new_expression_data();
				set_expression_data(se->expr,
						PRIMITIVE_TYPE_NAME_UNKNOWN,
						0);
				local_error = 1;
				error_type_mismatch(line_number, se->t->expr->type, se->next->expr->type);
			}
		} else {
			/* There were errors, either with the term or the simple
			   expression. Determine which one was ok, and use its datatype
			   for further propagation of type checking. */
			local_error = 1;

			if (retval_t == 0)
				se->expr = se->t->expr;
			else
				se->expr = se->next->expr;
		}
	} else {
		int retval_t;

		retval_t = check_used_variables_in_term(line_number, se->t, class, name_it_cannot_hold, scope, pass_number, in_left_hand_side, function_return_value_set);

		se->expr = se->t->expr;
		if (retval_t != 0)
			local_error = 1;
	}

	return local_error;
}



/* ----------------------------------------------------------------------- 
 * Checks if the variables used in the term are declared
 * ----------------------------------------------------------------------- 
 */
int check_used_variables_in_term(int line_number,
		struct term_t *t,
		char *class,
		char *name_it_cannot_hold, 
		int scope,
		int pass_number,
		int in_left_hand_side,
		int *function_return_value_set)
{
	int local_error = 0;

	if (t->next != NULL) {
		int retval_f;
		int retval_t;

		retval_f = check_used_variables_in_factor(line_number, t->f, class, name_it_cannot_hold, scope, pass_number, in_left_hand_side, function_return_value_set);
		retval_t = check_used_variables_in_term(line_number, t->next, class, name_it_cannot_hold, scope, pass_number, in_left_hand_side, function_return_value_set);

		if (retval_f == 0 && retval_t == 0) {

			/* do type checking */
			if (check_types(t->f->expr, t->next->expr)) {
				t->expr = new_expression_data();

				if (t->mulop == STAR) {
					set_expression_data(t->expr,
							t->f->expr->type,
							t->f->expr->val * t->next->expr->val);
				} else if (t->mulop == SLASH) {
					set_expression_data(t->expr,
							t->f->expr->type,
							t->f->expr->val / t->next->expr->val);
				} else if (t->mulop == MOD) {
                    int val = 0;
                    if ((int) t->next->expr->val != 0)
                        val = (int) t->f->expr->val % (int) t->next->expr->val;
					set_expression_data(t->expr,
							PRIMITIVE_TYPE_NAME_INTEGER, val);
				} else if (t->mulop == AND) {
					set_expression_data(t->expr,
							PRIMITIVE_TYPE_NAME_INTEGER,
							(int) t->f->expr->val & (int) t->next->expr->val);
				} else {
					local_error = 1;
					error_unknown(line_number);
				}
			} else {
				t->expr = new_expression_data();
				set_expression_data(t->expr,
						PRIMITIVE_TYPE_NAME_UNKNOWN,
						0);
				local_error = 1;
				error_type_mismatch(line_number, t->f->expr->type, t->next->expr->type);
			}
		} else {
			/* There were errors, either with the factor or the
			   term. Determine which one was ok, and use its datatype for
			   further propagation of type checking. */
			local_error = 1;

			if (retval_t == 0)
				t->expr = t->f->expr;
			else
				t->expr = t->next->expr;
		}
	} else {
		int retval_f;

		retval_f = check_used_variables_in_factor(line_number, t->f, class, name_it_cannot_hold, scope, pass_number, in_left_hand_side, function_return_value_set);
		if (retval_f != 0)
			local_error = 1;

		t->expr = t->f->expr;
	}

	return local_error;
}



/* ----------------------------------------------------------------------- 
 * Checks if the variables used in the factor are declared

 * ----------------------------------------------------------------------- 
 */
int check_used_variables_in_factor(int line_number,
		struct factor_t *f,
		char *class,
		char *name_it_cannot_hold,
		int scope,
		int pass_number,
		int in_left_hand_side,
		int *function_return_value_set)
{
	int local_error = 0;

	if (f->type == FACTOR_T_SIGNFACTOR) {
		if (f->data.f.next->type != FACTOR_T_SIGNFACTOR) {
			int retval_f;

			retval_f = check_used_variables_in_factor(line_number, f->data.f.next, class, name_it_cannot_hold, scope, pass_number, in_left_hand_side, function_return_value_set);

			if (retval_f == 0) {

				f->expr = new_expression_data();
				set_expression_data(f->expr,
						f->data.f.next->expr->type,
						f->data.f.next->expr->val);

				if (*f->data.f.sign == MINUS) {
					f->expr->val = - f->expr->val;
				}
			} else {
				f->expr = f->data.f.next->expr;
				local_error = 1;
			}

		} else {
			local_error = 1;
			error_too_many_signs(line_number);
		}
	} else if (f->type == FACTOR_T_PRIMARY) {
		int retval_p;

		retval_p = check_used_variables_in_primary(line_number, f->data.p, class, name_it_cannot_hold, scope, pass_number, in_left_hand_side, function_return_value_set);

		if (retval_p != 0)
			local_error = 1;

		f->expr = f->data.p->expr;
	}

	return local_error;
}



/* ----------------------------------------------------------------------- 
 * Checks if the variables used in the primary are declared
 * ----------------------------------------------------------------------- 
 */
int check_used_variables_in_primary(int line_number,
		struct primary_t *p,
		char *class,
		char *name_it_cannot_hold,
		int scope,
		int pass_number,
		int in_left_hand_side,
		int *function_return_value_set)
{
	int local_error = 0;

	if (p->type == PRIMARY_T_VARIABLE_ACCESS) {
		int retval_va;
		char *class_to_check;

		retval_va = check_used_variables_in_variable_access(line_number, p->data.va, class, name_it_cannot_hold, scope, pass_number, in_left_hand_side, function_return_value_set, &class_to_check, 1);

		if (retval_va == 0)
			/* used for type checking */
			p->expr = p->data.va->expr;
		else
			local_error = 1;
	} else if (p->type == PRIMARY_T_UNSIGNED_CONSTANT) {
		/* used for type checking */
		p->expr = p->data.un->expr;
	} else if (p->type == PRIMARY_T_FUNCTION_DESIGNATOR) {
		struct symbol_t *s;
		struct actual_parameter_list_t *apl;
		struct expression_data_t *ed;

		/* check if the datatypes of the function's parameters
		 * are defined */
		apl = p->data.fd->apl;
		while (apl != NULL) {
			int retval_e1;

			retval_e1 = check_used_variables_in_expression(line_number, apl->ap->e1, class, name_it_cannot_hold, scope, pass_number, in_left_hand_side, function_return_value_set);
			if (retval_e1 == 0) {

				if (apl->ap->e2 != NULL) {
					int retval_e2;

					retval_e2 = check_used_variables_in_expression(line_number, apl->ap->e2, class, name_it_cannot_hold, scope, pass_number, in_left_hand_side, function_return_value_set);
					if (retval_e2 == 0) {
						if (apl->ap->e3 != NULL) {
							int retval_e3;

							retval_e3 = check_used_variables_in_expression(line_number, apl->ap->e3, class, name_it_cannot_hold, scope, pass_number, in_left_hand_side, function_return_value_set);

							if (retval_e3 != 0)
								local_error = 1;
						}
					}
				} else {
					local_error = 1;
				}
			} else {
				local_error = 1;
			}
			apl = apl->next;
		}

		/* check if the function has been declared */
		s = symtab_function_desig_lookup(p->data.fd, class);
		if (s != NULL) {
			/* used for type checking */
			ed = new_expression_data();
			set_expression_data(ed,
					s->data.fd->fd->fh->res,
					0);
			p->expr = ed;
		} else {
			local_error = 1;
			error_function_not_declared(line_number, p->data.fd->id);
		}
	} else if (p->type == PRIMARY_T_EXPRESSION) {
		int retval_e;

		retval_e = check_used_variables_in_expression(line_number, p->data.e, class, name_it_cannot_hold, scope, pass_number, in_left_hand_side, function_return_value_set);

		if (retval_e == 0)
			/* used for type checking */
			p->expr = p->data.e->expr;
		else
			local_error = 1;
	} else if (p->type == PRIMARY_T_PRIMARY) {
		struct expression_data_t *ed;
		int retval_p;

		retval_p = check_used_variables_in_primary(line_number, p->data.p.next, class, name_it_cannot_hold, scope, pass_number, in_left_hand_side, function_return_value_set);

		if (retval_p == 0) {
			/* used for type checking */
			ed = new_expression_data();
			set_expression_data(ed, PRIMITIVE_TYPE_NAME_BOOLEAN, 0);
			p->expr = ed;

			if (check_types(p->expr, ed)) {
				p->expr->val = ! p->expr->val;
			}
			else {
				local_error = 1;
				error_datatype_is_not(line_number, p->expr->type, PRIMITIVE_TYPE_NAME_BOOLEAN);
			}
		} else {
			local_error = 1;
		}
	}

	return local_error;
}



/* ----------------------------------------------------------------------- 
 * Carries out semantic analysys on a program
 * ----------------------------------------------------------------------- 
 */
void semantic_analysis(struct program_t *p)
{
	struct class_list_t *temp_cl;
	int program_class_found = 0;


	/* The very first thing we will do is build up *only* the list of
	   datatypes and variable declarations. Then we will perform a
	   second pass where semantic analysis will be carried out. In that
	   pass, it will be possible to accept object instantiations (class
	   variables) that were declared before their class was defined. */
	temp_cl = p->cl;
	while (temp_cl != NULL) {
		struct usrdef_t *u;
		int class_scope = symtab_new_scope();

		/* Add this class as a datatype */
		u = usrdef_lookup(temp_cl->ci->id);
		if (u == NULL) {
			u = usrdef_insert(temp_cl->ci->id, temp_cl->ci->line_number);
			u->td = new_type_denoter();
			u->td->type = TYPE_DENOTER_T_CLASS_TYPE;
			u->td->data.cl = temp_cl;
			u->scope = class_scope;
		} else {
			error_class_already_declared(temp_cl->ci->line_number, temp_cl->ci->id, u->line_number);
			goto continue_loop_pass1;
		}

		/* process the variable_declaration_list */
		create_variables(temp_cl, u->scope, 1);

		/* process the func_declaration_list */
		create_functions(temp_cl, u->scope, 1);

continue_loop_pass1:
		temp_cl = temp_cl->next;
	}




	/* Perform the second pass. Carry out semantic analysis. This is
	   where all the fun begins. */
	temp_cl = p->cl;
	while (temp_cl != NULL) {
		struct usrdef_t *u;
		struct usrdef_t *u_ext;

		/* There should be at least one class with a name same as the
		   program name. Else we would not know which class to start
		   executing first. */
		if (strcmp(tolower(temp_cl->ci->id), tolower(p->ph->id)) == 0)
			program_class_found = 1;

		/* Since all class datatypes were added already, we should be able
		   to find this one */
		u = usrdef_lookup(temp_cl->ci->id);
		if (u->seen_at_least_once == 1) {
			/* This class has already been declared. Skip it's processing */
			goto continue_loop_pass2;
		}

		/* Ensure the class extended by this class exists */
		if (temp_cl->ci->extend != NULL) {
			u_ext = usrdef_lookup(temp_cl->ci->extend);
			if (u_ext == NULL) {
				error_extending_missing_class(temp_cl->ci->line_number, temp_cl->ci->id, temp_cl->ci->extend);
				goto continue_loop_pass2;
			}
		}

		/* process the variable_declaration_list */
		create_variables(temp_cl, u->scope, 2);

		/* process the func_declaration_list */
		create_functions(temp_cl, u->scope, 2);

continue_loop_pass2:
		u->seen_at_least_once = 1;
		temp_cl = temp_cl->next;
	}

	if (program_class_found == 0)
		error_missing_program_class();
}



/* -----------------------------------------------------------------------
 * Examines a function heading and does all the necessary processing
 * -----------------------------------------------------------------------
 */
void process_function_heading(int line_number,
		struct function_heading_t *fh,
		char *class,
		int scope,
		int pass_number)
{
	struct formal_parameter_section_list_t *temp;

	temp = fh->fpsl;
	while (temp != NULL) {
		struct identifier_list_t *il;

		il = temp->fps->il;
		while (il != NULL) {
			struct symbol_t *new;
			struct usrdef_t *u;

			if (pass_number == 1) {
				new = symtab_insert(il->id, class, scope, line_number, fh->id);
				new->type = SYMTAB_VARIABLE;
				u = usrdef_lookup(temp->fps->id);
				if (u != NULL) {
					new->data.type_name = u->name;
				}
				else {
					/* It could be the case that this data type is a class that
					   will be defined later on. Don't complain just yet. */
				}

			} else {
				/* This is pass_number 2 */
				u = usrdef_lookup(temp->fps->id);
				if (u == NULL)
					error_type_not_defined(line_number, temp->fps->id);
			}
			il = il->next;
		}

		temp = temp->next;
	}

}




/* ----------------------------------------------------------------------- 
 * Checks if the data types of se1 and se2 match
 * ----------------------------------------------------------------------- 
 */
int check_types_simple_expression(struct simple_expression_t *se1,
		struct simple_expression_t *se2)
{
	if (se2 == NULL)
		return check_types(se1->expr, NULL);
	else
		return check_types(se1->expr, se2->expr);
}



/* ----------------------------------------------------------------------- 
 * Checks if the data types of se1 and se2 match
 * ----------------------------------------------------------------------- 
 */
int check_types_term(struct term_t *t1,
		struct term_t *t2)
{
	if (t2 == NULL)
		return check_types(t1->expr, NULL);
	else
		return check_types(t1->expr, t2->expr);
}



/* -----------------------------------------------------------------------
 * Checks if the expression_data types ed1 and ed2 match Returns 1 if
 * they match, 0 if they don't
 * -----------------------------------------------------------------------
 */
int check_types(struct expression_data_t *ed1,
		struct expression_data_t *ed2)
{
	if ( !(ed1 == NULL && ed2 == NULL)) {

		if (ed1 == NULL) {
			return 1;
		}
		else if (ed2 == NULL) {
			return 1;
		}

		if (ed1->type == NULL) {
			return 1;
		}
		else if (ed2->type == NULL) {
			return 1;
		}

		if (check_types_types(ed1->type, ed2->type))
			return 1;

		return 0;
	}
	else
		return 1;
}



/* -----------------------------------------------------------------------
 * Checks if the types type1 and type2 are equivalent.  Returns 1 if
 * they match, 0 if they don't
 * -----------------------------------------------------------------------
 */
int check_types_types(char *type1, char *type2)
{
	if (strcmp(type1, type2) == 0 ) {
		return 1;
	}

	/* It could be the case that the two types are arrays of the same
	   size and type */
	if (check_types_structurally_equivalent_arrays(type1, type2))
		return 1;

	/* It could be the case that these two classes are
	   structurally equivalent. */
	if (check_structurally_equivalent_classes(type1, type2))
		return 1;

	return 0;
}



/* -----------------------------------------------------------------------
 * Checks if the types type1 and type1 are structurally equivalent
 * arrays. Returns 1 if they match, 0 if they don't
 * -----------------------------------------------------------------------
 */
int check_types_structurally_equivalent_arrays(char *type1, char *type2)
{
	struct usrdef_t *u1;
	struct usrdef_t *u2;

	u1 = usrdef_lookup(type1);
	u2 = usrdef_lookup(type2);

	if (u1->td->type == TYPE_DENOTER_T_ARRAY_TYPE &&
			u2->td->type == TYPE_DENOTER_T_ARRAY_TYPE &&
			check_types_types(u1->td->data.at->td->name, u2->td->data.at->td->name) == 1 &&
			(u1->td->data.at->r->max->ui - u1->td->data.at->r->min->ui ==
			 u2->td->data.at->r->max->ui - u2->td->data.at->r->min->ui))
		return 1;

	return 0;
}



/* -----------------------------------------------------------------------
 * Checks if the types type1 and type1 are structurally equivalent
 * arrays. Returns 1 if they match, 0 if they don't
 * -----------------------------------------------------------------------
 */
int check_structurally_equivalent_classes(char *type1, char *type2)
{
	struct usrdef_t *u1;
	struct usrdef_t *u2;

	u1 = usrdef_lookup(type1);
	u2 = usrdef_lookup(type2);

	if (u1 != NULL &&
			u2 != NULL &&
			u1->td->type == TYPE_DENOTER_T_CLASS_TYPE &&
			u2->td->type == TYPE_DENOTER_T_CLASS_TYPE) {
		struct variable_declaration_list_t *vdl1;
		struct variable_declaration_list_t *vdl2;

		vdl1 = u1->td->data.cl->cb->vdl;
		vdl2 = u2->td->data.cl->cb->vdl;

		while (vdl1 != NULL && vdl2 != NULL) {
			long il1_count;
			long il2_count;

			il1_count = identifier_list_count(vdl1->vd->il);
			il2_count = identifier_list_count(vdl2->vd->il);

			if ( !( vdl1->vd->tden->type == vdl2->vd->tden->type &&
						check_types_types(vdl1->vd->tden->name, vdl2->vd->tden->name) &&
						il1_count == il2_count) )
				return 0;

			vdl1 = vdl1->next;
			vdl2 = vdl2->next;
		}

		/* Must also check if the number of variables is the same. */
		if (vdl1 != NULL || vdl2 != NULL)
			return 0;

		/* This is great. They match. We must also check if they extend
		   other classes, and check those for equivalence, too. */
		if (u1->td->data.cl->ci->extend != NULL &&
				u2->td->data.cl->ci->extend != NULL) {
			/* These two classes both extend other classes */
			struct usrdef_t *u1_base;
			struct usrdef_t *u2_base;

			u1_base = usrdef_lookup(u1->td->data.cl->ci->extend);
			u2_base = usrdef_lookup(u2->td->data.cl->ci->extend);

			/* Check the base classes for equivalence, too */
			return check_types_types(u1->td->data.cl->ci->extend, u2->td->data.cl->ci->extend);
		} else if (u1->td->data.cl->ci->extend != NULL) {
			/* The first class was extending another class, and the second
			   wasn't. */
			return 0;
		} else if (u2->td->data.cl->ci->extend != NULL) {
			/* The second class was extending another class, and the first
			   wasn't. */
			return 0;
		} else {
			/* None of them extends another class. They are equivalent. */
			return 1;
		}
	}

	return 0;
}



/* -----------------------------------------------------------------------
 * Checks if a variable name is "True" or "False", which are built-in
 * values for boolean datatypes. Returns 1 if it is, 0 if it's not.
 * -----------------------------------------------------------------------
 */
int check_variable_tf(char *name)
{
	if (strcmp(tolower(name), tolower(BOOLEAN_VALUE_TRUE)) == 0 ||
			strcmp(tolower(name), tolower(BOOLEAN_VALUE_FALSE)) == 0) {
		return 1;
	}

	return 0;
}
